package crawler.web.engine;

import java.io.File;
import java.util.BitSet;

import library.utils.MD5;
import crawler.web.beans.CrawlURL;
import crawler.web.engine.config.ConfigBatchInit;

/**
 * @author waynechen Feb 7, 2012 10:03:10 PM
 */
public class SimpleBloomFilterHashBased implements VisitedFrontier {
	public static final int ELEM_NUM = 1024 * 1024 * 85; // 欲容纳的元素个数
	public static final double PERCENTAGE = 0.00001; // 希望的误差率 
	private int hashNum; // hash函数的数量 
	private int size; // 位向量的長度 
	private BitSet bitVecter; // 位向量 

	public SimpleBloomFilterHashBased(final File file) {
		bitVecter = ConfigBatchInit.loadBitSet(file);
		if (bitVecter == null) {
			size = (int) Math.abs(ELEM_NUM * Math.log(PERCENTAGE) / (Math.log(2) * Math.log(2))) + 1;
			hashNum = (int) (Math.log(2) * ((double) size / ELEM_NUM));
			bitVecter = new BitSet(size);
		}

		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() {
				ConfigBatchInit.saveBitSet(bitVecter, file);
			}
		});
	}

	/**
	 * 查找元素是否在集合中<br>
	 * 如果不在,则把元素加入集合中
	 */
	@Override
	public boolean findAndAdd(String url) {
		boolean flag = true;
		for (int i = 0; i < hashNum; i++) {
			long hash = processHash(url, i);
			// 元素不在集合中 
			if (!bitVecter.get((int) hash)) {
				bitVecter.set((int) hash);
				flag = false;
			}
		}
		return flag;
	}

	@Override
	public void add(CrawlURL value) {
		if (value != null)
			add(value.getOriUrl());
	}

	@Override
	public void add(String value) {
		for (int i = 0; i < hashNum; i++) {
			long hash = processHash(value, i);
			bitVecter.set((int) hash);
		}
	}

	@Override
	public boolean contains(CrawlURL value) {
		return contains(value.getOriUrl());
	}

	@Override
	public boolean contains(String value) {
		for (int i = 0; i < hashNum; i++) {
			long hash = processHash(value, i);
			// 元素不在集合中 
			if (!bitVecter.get((int) hash))
				return false;
		}
		return true;
	}

	private long processHash(String url, int i) {
		String input = url + i;
		//输入URL地址拼接上Hash函数的编号 
		long hash = MD5.getMD5(input, 64) % size;
		return hash;
	}
}
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//